﻿/**************************************************************************
创建时间:	2020/6/30 23:44:34    
作	  者:	张存
邮 	  箱:	zhangcunliang@126.com

Copyright (c) zhcun.cn

描	述：
记	录：
***************************************************************************/
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.OpenApi.Any;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text;
using ZhCun.Utils.Helpers;
using ZhCun.WebUtils.Configs;

namespace ZhCun.WebUtils
{
    public class BodyEncryptFilterAttribute : Attribute, IActionFilter, IResourceFilter
    {
        public BodyEncryptFilterAttribute(bool isEncrypt = true)
        {
            IsEncrypt = isEncrypt;
        }

        private static readonly ConcurrentDictionary<string, RSAHelper> RsaList = new ConcurrentDictionary<string, RSAHelper>();

        public bool IsEncrypt { set; get; }

        public string KeyFile { set; get; } = AppContext.BaseDirectory + CommonConfig.THIS.RsaBodyKey;

        /// <summary>
        /// RSA加密对象
        /// </summary>
        private RSAHelper RSA
        {
            get
            {
                if (!RsaList.TryGetValue(KeyFile, out RSAHelper rsa))
                {
                    string rsaKey = File.ReadAllText(KeyFile);
                    rsa = new RSAHelper(rsaKey);
                    RsaList.TryAdd(KeyFile, rsa);
                }
                return rsa;
            }
        }

        /// <summary>
        /// 本次请求加密 aes 密钥
        /// </summary>
        private string AesKey;

        private bool CheckFilter(FilterContext context)
        {
            if (!context.IsEffectivePolicy(this)) return false;

            if (!IsEncrypt) return false;

            return true;
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (!CheckFilter(context)) return;

            if (AesKey.IsEmpty() && CommonConfig.THIS.AllowPlaintext)
            {
                return;
            }

            if (context.Result is ObjectResult rObj && rObj.Value != null)
            {
                var jsonValue = JsonHelper.Serialize(rObj.Value);
                var enResult = DEncryptAES.Encrypt(jsonValue, AesKey);
                rObj.Value = enResult;
            }
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {

        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {

        }

        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            if (!CheckFilter(context)) return;

            AesKey = null;

            var encryptKey = context.HttpContext.GetHeader(CommonConfig.THIS.BodyEncryptKeyName);
            if (encryptKey.IsEmpty())
            {
                if (CommonConfig.THIS.AllowPlaintext)
                {
                    return;
                }
                context.Result = new ContentResult
                {
                    StatusCode = StatusCodes.Status400BadRequest,
                    Content = "body data format error"
                };
                return;
            }

            AesKey = RSA.Decrypt(encryptKey);
            var bodyStream = context.HttpContext.Request.BodyReader.AsStream(true);
            var bodyBuffer = new byte[context.HttpContext.Request.ContentLength.Value];
            bodyStream.Read(bodyBuffer, 0, bodyBuffer.Length);
            var bodyContent = Encoding.UTF8.GetString(bodyBuffer);
            var bodyBytes = DEncryptAES.DecryptBytes(bodyContent, AesKey);
            //使用 System.Text.Json.JsonSerializer 访问时有这种问题，使用NewtonsoftJson 则没问题
            int i;
            for (i = bodyBytes.Length - 1; i >= 0; i--)
            {
                if (bodyBytes[i] != 0) break;
            }
            context.HttpContext.Request.Body = new MemoryStream(bodyBytes, 0, i + 1);
        }
    }
}